package dev.ece.suit.loader;

import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL11;

import dev.ece.core.vertex.EBO;
import dev.ece.core.vertex.VAO;
import dev.ece.core.vertex.VBO;
import dev.ece.util.lib.Vector2f;
import dev.ece.util.lib.Vector3f;
import dev.ece.util.lib.Vector4f;

/**
 * 网格
 * @author 潜行的虚影
 *
 */
public class Mesh {

	private List<Vector3f> vertice = new ArrayList<Vector3f>();
	
	private List<Vector4f> boneindice = new ArrayList<Vector4f>();
	
	private List<Vector4f> weights = new ArrayList<Vector4f>();
	
	private List<Vector2f> textureCoords = new ArrayList<Vector2f>();
	
	private List<Vector3f> normals = new ArrayList<Vector3f>();
	
	private int[] faceIndice;
	
	private int materialIndex;
	
	private boolean existMaterial;
	
	private VAO vao;
	
	private VBO verticeVBO;
	
	private VBO boneindiceVBO;
	
	private VBO weightsVBO;
	
	private VBO textureCoordsVBO;
	
	private VBO normalsVBO;
	
	private EBO renderEBO;
	
	private int count;
	
	public Mesh(int[] faceIndice, int materialIndex) {
		this.faceIndice = faceIndice;
		this.setMaterialIndex(materialIndex);
	}
	
	protected void setup(Face[] faces) {
		renderEBO = createRenderEBO(faces);
		verticeVBO = VBO.newInstance3f(vertice);
		if(!boneindice.isEmpty()) {
			boneindiceVBO = VBO.newInstance4f(boneindice);
		}
		if(!weights.isEmpty()) {
			weightsVBO = VBO.newInstance4f(weights);
		}
		textureCoordsVBO = VBO.newInstance2f(textureCoords);
		normalsVBO = VBO.newInstance3f(normals);
		this.count = createCount();
		this.clearOriginalData();
	}
	
	protected void initialize() {
		this.vao = createVAO();
		
		verticeVBO = null;
		boneindiceVBO = null;
		weightsVBO = null;
		textureCoordsVBO = null;
		normalsVBO = null;
	}
	
	protected EBO createRenderEBO(Face[] faces) {
		List<Integer> indice = new ArrayList<Integer>();
		for(Face face: faces) {
			for(int index: face.getIndice()) {
				indice.add(index);
			}
		}
		return EBO.newInstanceInt(indice);
	}
	
	protected int createCount() {
		return this.vertice.size();
	}
	
	protected VAO createVAO() {
		VAO vao = new VAO();
		vao.bind();
		vao.bindVertexArrayBuffer(verticeVBO, 3);
		if(boneindiceVBO != null) {
			vao.bindVertexArrayBuffer(boneindiceVBO, 4);
		}
		if(weightsVBO != null) {
			vao.bindVertexArrayBuffer(weightsVBO, 4);
		}
		vao.bindVertexArrayBuffer(textureCoordsVBO, 2);
		vao.bindVertexArrayBuffer(normalsVBO, 3);
		vao.finish();
		return vao;
	}
	
	protected void renderDepthMap() {
		vao.bind();
		render();
	}
	
	protected void render() {
		this.renderEBO.bind();
		this.renderEBO.glDrawElements(GL11.GL_TRIANGLES);
	}
	
	protected void render(ModelShader modelShader, Material[] materials) {
		if(this.isExistMaterial()) {
			modelShader.glMaterial(materials[this.getMaterialIndex()]);
		}
		renderDepthMap();
	}

	public int getCount() {
		return count;
	}

	public List<Vector3f> getVertice() {
		return vertice;
	}

	public List<Vector2f> getTextureCoords() {
		return textureCoords;
	}

	public List<Vector3f> getNormals() {
		return normals;
	}

	public List<Vector4f> getBoneindice() {
		return boneindice;
	}

	public List<Vector4f> getWeights() {
		return weights;
	}

	protected void setVertice(List<Vector3f> vertice) {
		this.vertice = vertice;
	}

	protected void setTextureCoords(List<Vector2f> textureCoords) {
		this.textureCoords = textureCoords;
	}

	protected void setNormals(List<Vector3f> normals) {
		this.normals = normals;
	}

	public int[] getFaceIndice() {
		return faceIndice;
	}

	public void setFaceIndice(int[] faceIndice) {
		this.faceIndice = faceIndice;
	}

	protected void setBoneindice(List<Vector4f> boneindice) {
		this.boneindice = boneindice;
	}

	protected void setWeights(List<Vector4f> weights) {
		this.weights = weights;
	}

	public int getMaterialIndex() {
		return materialIndex;
	}

	protected void setMaterialIndex(int materialIndex) {
		this.materialIndex = materialIndex;
		this.existMaterial = materialIndex > -1;
	}

	public boolean isExistMaterial() {
		return existMaterial;
	}

	protected void clearOriginalData() {
		if(vertice != null) {
			vertice.clear();
			vertice = null;
		}
		if(boneindice != null) {
			boneindice.clear();
			boneindice = null;
		}
		if(weights != null) {
			weights.clear();
			weights = null;
		}
		if(textureCoords != null) {
			textureCoords.clear();
			textureCoords = null;
		}
		if(normals != null) {
			normals.clear();
			normals = null;
		}
	}
	
	public void destroy() {
		if(vao != null) {
			vao.destroy();
			vao = null;
		}
		if(renderEBO != null) {
			renderEBO.destroy();
			renderEBO = null;
		}
	}
	
}
